/**
 * Copyright 2015 bingoogolapple
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.bingoogolapple.badgeview;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.agp.utils.TextAlignment;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.image.PixelMap;
import ohos.media.image.common.Position;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

import java.lang.ref.WeakReference;
import java.util.Optional;

/**
 * BGADragBadgeView
 *
 * @since 2021-04-21
 */
public class BGADragBadgeView extends Component implements Component.TouchEventListener {

    private BGABadgeViewHelper mBadgeViewHelper;
    private Paint mBadgePaint;
    private int mStartX;
    private int mStartY;
    private BGAExplosionAnimator mExplosionAnimator;
    private SetExplosionAnimatorNullTask mSetExplosionAnimatorNullTask;

    /**
     * 顶层的View
     */
    private Component topComponent;

    /**
     * Y坐标偏移高度
     */
    private int TOPTITLEHEIGHT = 0;

    /**
     * 针圆切线的切点
     */
    private Point[] mStickPoints = new Point[]{
            new Point(0, 0),
            new Point(0, 0)
    };
    /**
     * 拖拽圆切线的切点
     */
    private Point[] mDragPoints = new Point[]{
            new Point(0, 0),
            new Point(0, 0)
    };
    /**
     * 控制点
     */
    private Point mControlPoint = new Point(0, 0);
    /**
     * 拖拽圆中心点
     */
    private Point mDragCenter = new Point(0, 0);
    /**
     * 拖拽圆半径
     */
    private float mDragRadius;

    /**
     * 针圆中心点
     */
    private Point mStickCenter;
    /**
     * 针圆半径
     */
    private float mStickRadius;
    /**
     * 拖拽圆最大半径
     */
    private int mMaxDragRadius;
    /**
     * 拖拽圆半径和针圆半径的差值
     */
    private int mDragStickRadiusDifference;
    /**
     * 拖动mDismissThreshold距离后抬起手指徽章消失
     */
    private int mDismissThreshold;

    private boolean mDismissAble;
    private boolean mIsDragDisappear;

    /**
     * BGADragBadgeView
     *
     * @param context
     * @param badgeViewHelper
     */
    public BGADragBadgeView(Context context, BGABadgeViewHelper badgeViewHelper) {
        super(context);
        // 注册事件
        setTouchEventListener(this);
        mBadgeViewHelper = badgeViewHelper;
        initBadgePaint();
        initStick();

        mSetExplosionAnimatorNullTask = new SetExplosionAnimatorNullTask(this);
    }

    /**
     * 初始化画笔
     */
    private void initBadgePaint() {
        mBadgePaint = new Paint();
        mBadgePaint.setAntiAlias(true);
        mBadgePaint.setStyle(Paint.Style.FILL_STYLE);

        // 设置mBadgeText居中，保证mBadgeText长度为1时，文本也能居中
        mBadgePaint.setTextAlign(TextAlignment.CENTER);
        mBadgePaint.setTextSize(mBadgeViewHelper.getBadgeTextSize());
    }

    /**
     * initStick
     */
    private void initStick() {
        mMaxDragRadius = BGABadgeViewUtil.dp2px(getContext(), 10);
        mDragStickRadiusDifference = BGABadgeViewUtil.dp2px(getContext(), 1);
    }

    /**
     * DrawableBadge
     *
     * @param canvas
     */
    private void drawDrawableBadge(Canvas canvas) {
        canvas.drawPixelMapHolder(new PixelMapHolder(mBadgeViewHelper.getBitmap()), mStartX, mStartY, mBadgePaint);
    }

    /**
     * drawText
     *
     * @param canvas
     */
    private void drawTextBadge(Canvas canvas) {
        // 设置徽章背景色
        mBadgePaint.setColor(new Color(mBadgeViewHelper.getBadgeBgColor()));

        // 绘制徽章背景
        canvas.drawRoundRect(
                new RectFloat(
                        mStartX,
                        mStartY,
                        (float) NumberUtil.add(mStartX , mBadgeViewHelper.getBadgeRectF().getWidth()),
                        (float) NumberUtil.add(mStartY , mBadgeViewHelper.getBadgeRectF().getHeight())),
                (float) NumberUtil.div(mBadgeViewHelper.getBadgeRectF().getHeight() , 2),
                (float) NumberUtil.div(mBadgeViewHelper.getBadgeRectF().getHeight() , 2),
                mBadgePaint);

        // 设置徽章文本颜色
        mBadgePaint.setColor(new Color(mBadgeViewHelper.getBadgeTextColor()));
        double widthDiv = NumberUtil.div(mBadgeViewHelper.getBadgeRectF().getWidth() , 2);
        float x = (float) NumberUtil.add(mStartX , widthDiv);

        // 注意：绘制文本时的y是指文本底部，而不是文本的中间
        double heightAdd = NumberUtil.add(mStartY , mBadgeViewHelper.getBadgeRectF().getHeight());
        float y = (float) NumberUtil.sub(heightAdd , mBadgeViewHelper.getBadgePadding()) ;

        // 绘制徽章文本
        String badgeText = mBadgeViewHelper.getBadgeText() == null ? "" : mBadgeViewHelper.getBadgeText();
        canvas.drawText(mBadgePaint, badgeText, x, y);
    }

    /**
     * drawText
     *
     * @param canvas
     */
    private void drawStick(Canvas canvas) {
        float currentStickRadius = getCurrentStickRadius();

        // 2. 获取直线与圆的交点
        float yOffset = (float) NumberUtil.sub(mStickCenter.getPointY() , mDragCenter.getPointY()) ;
        float xOffset = (float) NumberUtil.sub(mStickCenter.getPointX() , mDragCenter.getPointX());
        Double lineK = null;
        if (xOffset != 0) {
            lineK = (double) (yOffset / xOffset);
        }

        // 通过几何图形工具获取交点坐标
        mDragPoints = BGABadgeViewUtil.getIntersectionPoints(mDragCenter, mDragRadius, lineK);
        mStickPoints = BGABadgeViewUtil.getIntersectionPoints(mStickCenter, currentStickRadius, lineK);

        // 3. 获取控制点坐标
        mControlPoint = BGABadgeViewUtil.getMiddlePoint(mDragCenter, mStickCenter);
        LogUtil.error("drawStick", "x=" + mControlPoint.getPointX() + " Y=" + mControlPoint.getPointY());

        if (!mIsDragDisappear) {
            if (!mDismissAble) {

                // 3. 画连接部分
                Path path = new Path();

                // 跳到点1
                path.moveTo(mStickPoints[0].getPointX(), mStickPoints[0].getPointY());

                // 画曲线1 -> 2
                path.quadTo(mControlPoint.getPointX(), mControlPoint.getPointY(), mDragPoints[0].getPointX(), mDragPoints[0].getPointY());

                // 画直线2 -> 3
                path.lineTo(mDragPoints[1].getPointX(), mDragPoints[1].getPointY());

                // 画曲线3 -> 4
                path.quadTo(mControlPoint.getPointX(), mControlPoint.getPointY(), mStickPoints[1].getPointX(), mStickPoints[1].getPointY());
                path.close();
                canvas.drawPath(path, mBadgePaint);

                // 2. 画固定圆
                canvas.drawCircle(mStickCenter.getPointX(), mStickCenter.getPointY(), currentStickRadius, mBadgePaint);
            }

            // 1. 画拖拽圆
            canvas.drawCircle(mDragCenter.getPointX(), mDragCenter.getPointY(), mDragRadius, mBadgePaint);
        }
    }

    /**
     * 获取针圆实时半径
     *
     * @return float
     */
    private float getCurrentStickRadius() {
        /**
         * distance 0 -> mDismissThreshold
         * percent 0.0f -> 1.0f
         * currentStickRadius mStickRadius * 100% -> mStickRadius * 20%
         */
        float distance = BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter);
        distance = Math.min(distance, mDismissThreshold);
        float percent = distance / mDismissThreshold;
        return BGABadgeViewUtil.evaluate(percent, mStickRadius, mStickRadius * 0.2f);
    }

    /**
     * setStickCenter
     *
     * @param x
     * @param y
     */
    public void setStickCenter(float x, float y) {
        mStickCenter = new Point(x, y);
    }

    /**
     * TouchEvent
     *
     * @param component
     * @param touchEvent
     * @return boolean
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        try {
            switch (touchEvent.getAction()) {
                case TouchEvent.PRIMARY_POINT_DOWN:
                    handleActionDown(touchEvent);
                    break;
                case TouchEvent.POINT_MOVE:
                    handleActionMove(touchEvent);
                    break;
                case TouchEvent.PRIMARY_POINT_UP:
                case TouchEvent.CANCEL:
                    handleActionUp(touchEvent);
                    break;
            }
        } catch (Exception e) {

            // 确保自己能被移除
            removeSelfWithException();
            LogUtil.error("onTouchEvent", "发生错误信息" + e.getMessage());
        }
        return true;
    }

    /**
     * handleActionDown
     *
     * @param touchEvent
     */
    private void handleActionDown(TouchEvent touchEvent) {
        MmiPoint point = touchEvent.getPointerScreenPosition(touchEvent.getIndex());
        if (mExplosionAnimator == null && getComponentParent() == null) {
            mDragRadius = Math.min(mBadgeViewHelper.getBadgeRectF().getWidth() / 2, mMaxDragRadius);
            mStickRadius = (float) NumberUtil.sub(mDragRadius , mDragStickRadiusDifference);
            mDismissThreshold = (int) (mStickRadius * 10);

            mDismissAble = false;
            mIsDragDisappear = false;

            LogUtil.error("initPoint", point.getX() + " Y" + point.getY());
            updateDragPosition(point.getX(), (float) NumberUtil.sub(point.getY() , TOPTITLEHEIGHT));
        }
    }

    /**
     * handleActionMove
     *
     * @param event
     */
    private void handleActionMove(TouchEvent event) {
        MmiPoint point = event.getPointerScreenPosition(event.getIndex());
        if (mExplosionAnimator == null) {
            updateDragPosition(point.getX(), (float) NumberUtil.sub(point.getY() , TOPTITLEHEIGHT));

            // 处理断开事件
            if (BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter) > mDismissThreshold) {
                mDismissAble = true;
                topComponent.invalidate();
            } else if (mBadgeViewHelper.isResumeTravel()) {
                mDismissAble = false;
                topComponent.invalidate();
            }
        }
    }

    /**
     * handleActionUp
     *
     * @param event
     */
    private void handleActionUp(TouchEvent event) {
        LogUtil.error("springAnim", "handleActionUp");
        handleActionMove(event);
        MmiPoint point = event.getPointerScreenPosition(event.getIndex());
        if (mDismissAble) {

            // 拖拽点超出过范围
            LogUtil.error("springAnim", "拖拽点超出过范围");
            if (BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter) > mDismissThreshold) {

                // 现在也超出范围,消失
                LogUtil.error("springAnim", "现在也超出范围,消失");
                try {
                    mIsDragDisappear = true;
                    int newStartX = getNewStartX(point.getX());
                    int newStartY = getNewStartY(point.getY());
                    startDismissAnim(newStartX, newStartY, event);
                } catch (Exception e) {
                    e.getMessage();
                    removeSelf();
                    mBadgeViewHelper.endDragWithDismiss();
                }
            } else {
                startSpringAnim();
            }
        } else {
            //	拖拽点没超出过范围,弹回去
            LogUtil.error("springAnim", "拖拽点没超出过范围,弹回去");
            try {
                startSpringAnim();
            } catch (Exception e) {
                LogUtil.error("springAnim", "出错了信息" + e.getMessage());
                removeSelf();
                mBadgeViewHelper.endDragWithoutDismiss();
            }
        }
    }

    /**
     * startAnim
     */
    private void startSpringAnim() {
        final Point startReleaseDragCenter = new Point(mDragCenter.getPointX(), mDragCenter.getPointY());

        AnimatorValue springAnim = new AnimatorValue();

        // 差值器类型
        springAnim.setCurveType(AnimatorValue.CurveType.OVERSHOOT);

        // 循环次数
        springAnim.setLoopedCount(1);

        // 动画时长
        springAnim.setDuration(100);

        // 数值变化
        springAnim.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {

                // 0.0 -> 1.0f
                LogUtil.error("startSpringAnim", v + "");
                Point p = BGABadgeViewUtil.getPointByPercent(startReleaseDragCenter, mStickCenter, v);
                updateDragPosition(p.getPointX(), p.getPointY());
            }
        });


        springAnim.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {
                Point point = new Point(0, 0);
                mStickCenter = point;
                updateDragPosition(0, 0);
                mBadgeViewHelper.endDragWithoutDismiss();
            }

            @Override
            public void onEnd(Animator animator) {
                mStickCenter = null;
                mBadgeViewHelper.endDragWithoutDismiss();
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        springAnim.start();
    }

    /**
     * startDismissAnim
     *
     * @param newX
     * @param newY
     * @param event
     */
    private void startDismissAnim(int newX, int newY, TouchEvent event) {
        MmiPoint point = event.getPointerScreenPosition(event.getIndex());
        int badgeWidth = (int) mBadgeViewHelper.getBadgeRectF().getWidth();
        int badgeHeight = (int) mBadgeViewHelper.getBadgeRectF().getHeight();
        int minX = newX - badgeWidth / 2;
        int minY = newY - badgeHeight / 2;
        int width = newX + badgeWidth / 2;
        int height = newY + badgeHeight / 2;

        // 确定爆炸矩形范围
        Rect rect = new Rect(minX, (int) (NumberUtil.sub(point.getY() , TOPTITLEHEIGHT)  - NumberUtil.div(badgeHeight , 2)),
                width,
                (int) (NumberUtil.sub(point.getY() , TOPTITLEHEIGHT) + NumberUtil.div(badgeHeight , 2)));
        LogUtil.error("startDismissAnim", "minX" + minX + "minY" + minY + "width" + width + "height" + height);

        rect.top = rect.top - 50;
        rect.bottom = rect.bottom + 50;
        rect.left = rect.left - 50;
        rect.right = rect.right + 50;

        PixelMap badgeBitmap = BGABadgeViewUtil.createBitmapSafely(topComponent, rect, 1);
        if (badgeBitmap == null) {
            removeSelf();
            mBadgeViewHelper.endDragWithDismiss();
            return;
        }

        if (mExplosionAnimator != null) {
            removeSelf();
            mBadgeViewHelper.endDragWithDismiss();
            return;
        }

        if (mBadgeViewHelper.isShowDrawable()) {

            // 图片
            mExplosionAnimator = new BGAExplosionAnimator(topComponent, rect, mBadgePaint.getColor().getValue());
        } else {

            // 背景Color设置文字徽章爆炸效果
            mExplosionAnimator = new BGAExplosionAnimator(topComponent, rect, mBadgeViewHelper.getBadgeBgColor());
        }

        mExplosionAnimator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {
                removeSelf();
                mBadgeViewHelper.endDragWithDismiss();
            }

            @Override
            public void onEnd(Animator animator) {
                removeSelf();
                mBadgeViewHelper.endDragWithDismiss();
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        mExplosionAnimator.start();
    }

    /**
     * removeSelf
     */
    private void removeSelf() {
        mDismissAble = false;
        mIsDragDisappear = false;

        // 处理有时候爆炸效果结束后出现一瞬间的拖拽效果
        EventRunner runner = EventRunner.create(false);
        EventHandler myHandler = new EventHandler(runner);
        myHandler.postTask(mSetExplosionAnimatorNullTask, 60);
    }

    /**
     * 修改拖拽位置
     *
     * @param rawX float
     * @param rawY float
     */
    private void updateDragPosition(float rawX, float rawY) {
        mStartX = getNewStartX(rawX);
        mStartY = getNewStartY(rawY);

        Point point = new Point(rawX, rawY);
        mDragCenter = point;
        topComponent.invalidate();
    }

    /**
     * getNewStartX
     *
     * @param rawX
     * @return int
     */
    private int getNewStartX(float rawX) {
        int badgeWidth = (int) mBadgeViewHelper.getBadgeRectF().getWidth();
        int newX = (int) rawX - badgeWidth / 2;
        if (newX < 0) {
            newX = 0;
        }
        Optional<Display> display = DisplayManager.getInstance().getDefaultDisplay(this.getContext());
        DisplayAttributes displayAttributes = null;
        if (display.isPresent()) {
            displayAttributes = display.get().getAttributes();
            if (newX > displayAttributes.width - badgeWidth) {
                newX = displayAttributes.width - badgeWidth;
            }
        }
        return newX;
    }

    /**
     * getNewStartY
     *
     * @param rawY
     * @return NewStartY
     */
    private int getNewStartY(float rawY) {
        int badgeHeight = (int) mBadgeViewHelper.getBadgeRectF().getHeight();
        Optional<Display> display = DisplayManager.getInstance().getDefaultDisplay(this.getContext());
        DisplayAttributes displayAttributes = null;
        int maxNewY = 0;
        if (display.isPresent()) {
            displayAttributes = display.get().getAttributes();
            maxNewY = displayAttributes.height - badgeHeight;
        }
        int newStartY = (int) rawY - badgeHeight / 2;
        LogUtil.error("getNewStartY", "badgeHeight" + badgeHeight + "maxNewY" + maxNewY + "newStartY" + newStartY);
        return Math.min(Math.max(0, newStartY), maxNewY);
    }

    /**
     * removeSelfWithException
     */
    private void removeSelfWithException() {
        removeSelf();
        if (BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter) > mDismissThreshold) {
            mBadgeViewHelper.endDragWithDismiss();
        } else {
            mBadgeViewHelper.endDragWithoutDismiss();
        }
    }

    /**
     * Runnable
     */
    private static class SetExplosionAnimatorNullTask implements Runnable {
        private final WeakReference<BGADragBadgeView> mDragBadgeView;

        public SetExplosionAnimatorNullTask(BGADragBadgeView dragBadgeView) {
            mDragBadgeView = new WeakReference<>(dragBadgeView);
        }

        @Override
        public void run() {
            BGADragBadgeView dragBadgeView = mDragBadgeView.get();
            if (dragBadgeView != null) {
                dragBadgeView.mExplosionAnimator = null;
            }
        }
    }

    /**
     * ViewDraw
     *
     * @param canvas
     */
    public void onViewDraw(Canvas canvas) {
        LogUtil.error("onDraw", "执行onDraw方法");
        try {
            if (mExplosionAnimator == null) {
                if (mBadgeViewHelper.isShowDrawable()) {
                    if (mBadgeViewHelper.getBadgeBgColor() == Color.getIntColor("#d71345")) {
                        Position position = new Position();
                        position.posX = mBadgeViewHelper.getBitmap().getImageInfo().size.width / 2;
                        position.posY = mBadgeViewHelper.getBitmap().getImageInfo().size.height / 2;
                        mBadgePaint.setColor(new Color(mBadgeViewHelper.getBitmap().readPixel(position)));
                    } else {
                        mBadgePaint.setColor(new Color(mBadgeViewHelper.getBadgeBgColor()));
                    }
                    drawStick(canvas);
                    drawDrawableBadge(canvas);
                } else {
                    mBadgePaint.setColor(new Color(mBadgeViewHelper.getBadgeBgColor()));
                    drawStick(canvas);
                    drawTextBadge(canvas);
                }
            } else {
                mExplosionAnimator.draw(canvas);
            }
        } catch (Exception e) {

            // 确保自己能被移除
            LogUtil.error("onDraw", "发生错误信息" + e.getMessage());
        }
    }

    /**
     * setComponent
     *
     * @param topComponent
     */
    public void setComponent(Component topComponent) {
        this.topComponent = topComponent;
        TOPTITLEHEIGHT = topComponent.getLocationOnScreen()[1];
    }

    /**
     * setMDrawTask
     *
     * @param canvas
     */
    public void setMDrawTask(Canvas canvas) {
        onViewDraw(canvas);
    }
}
